#pragma once
#include "stdafx.h"
#include "DO.h"

/** Default constructor. Sets value of the "Length" field to be Zero(0) and "Value" field to be NULL.*/
DO::DO(void)			
{
	length = 0;
	value = NULL;
}

/** Default destructor. Frees the memory occupied by "Value" field.*/
DO::~DO(void)			
{
	if(value != NULL) free(value);
	value = NULL;
}

/** Constructor. Sets the value of the "Tag" field with the given tag_arg and initializes "Length" to 0 and "Value" to NULL.*/
DO::DO(WORD tag_arg 			///< Tag argument.
	)	
{
	tag = tag_arg;
	length = 0;
	value = NULL;
}

/** Sets the TLV with the corresponding arguments.
\note "Length" must contain the exact number of bytes in "Value" field. */
DO::DO(const BYTE tag_arg[], 	///< Tag argument as an array.
		WORD length_arg,		///< length of value field.
		const BYTE *value_arg	///< Value argument as an array. 
		)
{
	setTag(tag_arg);
	value = NULL;
	length = 0;
	setValue(length_arg, value_arg);
}
/** Sets the TLV with the corresponding arguments.
\note "Length" must contain the exact number of bytes in "Value" field.
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);
\endexample
*/
DO::DO(WORD tag_arg, 			///< Tag argument.
		WORD length_arg,		///< length of value field.
		const BYTE *value_arg 	///< Value argument as an array.
		) 
{
	tag = tag_arg;
	value = NULL;
	length = 0;
	setValue(length_arg, value_arg);
}

/** Sets the "Tag" and one BYTE "Value" field with the corresponding arguments, "Length" is implicitly assumed to be 1.
\example
BYTE tag_arg = 0x19;
BYTE value_arg = 0x01;
DO do_obj(tag_arg, value_arg);
\endexample
*/
DO::DO(WORD tag_arg, 		///< Tag argument. 
	 	BYTE value_arg 		///< One BYTE(8-bit) Value argument.
		)
{
	tag = tag_arg;
	value = NULL;
	length = 0;
	setValue(1, &value_arg); 
}

/** Initializes the TLV by extracting the values from the Byte-Stream 
consisting of the concatenation of TLV fields.
\example
BYTE data[10];
data[0] = 0x19;
data[1] = 0x08;
for(int z = 0 ; z < 8 ; z++){
	data[z+2] = z;
}	
DO card(data);
printf("%x \n", card.getLength());

$$ 8
\endexample
*/
DO::DO(const BYTE *byte_stream		///< Array of bytes from which DO's various fields has
									///  to be extracted to initialize TLV.
		)
{
	int i=1, len;
    value = NULL;
	length = 0;
	setTag(byte_stream);
	if (tag > 0xFF) i = 2;
	len = byte_stream[i++];
	if (len == (BYTE) 0x82){
		int a = byte_stream[i++] * 256;
		len = a + byte_stream[i++];
	}
	else if(len == (BYTE)0x81) {
		len = byte_stream[i++];
	}
	setValue(len, byte_stream+i);
}

/** Copy Constructor. Creates a new copy of the given DO. 
\example
BYTE tag_arg = 0x19;
BYTE value_arg = 0x01;
DO do_obj(tag_arg, value_arg);
DO do_copy(do_obj);
printf("%x \n", do_copy.getLength());

$$ 1
\endexample
*/
DO::DO(const DO &do_arg  	///< Reference to the DO object whose copy is to be made.
	   )
{
	tag = do_arg.tag;
	value = NULL;
	length = 0;
	setValue(do_arg.length, do_arg.value);
}

/** To set the "Tag" of DO with the value of tag_arg. 
\example
Please see other variant of this function for example.
\endexample
*/
void DO::setTag(const BYTE *tag_arg	 	///< Tag argument as an array. 
				)	
{
	if((tag_arg[0] & 0x1F) == 0x1F)
		tag = tag_arg[0] * 256 + tag_arg[1];
	else
		tag = tag_arg[0];
}

/** Sets the "Tag" of DO with the value of tag_arg. 
\example
DO do_obj(0x19, 0x01, 0x01);
printf("%x", do_obj.getTag());

$$ 19

do_obj.setTag(0x88);
printf("%x", do_obj.getTag());

$$ 88
\endexample
*/
void DO::setTag(const WORD tag_arg		///< Tag argument. 
				)
{
	tag = tag_arg;
}

/** Sets the "Value" and "Length" fields with the corresponding arguments.
Previous values of "Length" and "Value" fields would be overwritten with the new arguments.
\note "Length" must contain the exact number of bytes in "Value" field. 
\example
DO do_obj(0x19, 0x01, 0x01);
printf("%x", do_obj.getLength());

$$ 1

BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
do_obj.setValue(len_arg, value_arg);
printf("%x", do_obj.getLength());

$$ 5
\endexample
*/
void DO::setValue(WORD length_arg,			///< length of the value field.	
				  const BYTE *value_arg		///< Value argument.
				  )
{
	/*BYTE* q = (BYTE *) malloc(length_arg + VALUECHUNK);
	if(length_arg >= 0 && q){
		memcpy(q,value_arg,length_arg);
		memset(q + length_arg,0,VALUECHUNK);
		free(value);
		length = length_arg;
		value=q;
	}
	else 
		throw "DOs could not be constructed.";*/

	if ((VALUECHUNK*((length + VALUECHUNK - 1)/VALUECHUNK)) <= length_arg) 
		value = (BYTE *) realloc(value, VALUECHUNK * ((length_arg+VALUECHUNK-1)/VALUECHUNK));		
	length = length_arg;
	if(value)
		memcpy(value, value_arg, length);
}

/** Sets the "Value' and "Length" of the parent DO(now constructed DO) with the collection of DO's passed as List.
Previous values of "Length" and "Value" fields would be overwritten with the new arguments.
\example
\endexample
*/
void DO::setConstructedValue(const list<DO>& collection		///< Argument passed as collection of DO's
															///< using inbuilt "list" template. 
							 )
{
	BYTE *buffer = NULL;
	int bufferlen = 0;
	int temp;
	
	for (list<DO>::const_iterator p = collection.begin(); p!=collection.end(); p++) {
		temp = p->getTotalLength();
		if (bufferlen < temp) {
			buffer  = (BYTE *) realloc(buffer, temp);
			bufferlen = temp;
		}
		p->getTotalBytes(buffer);
		appendValue(temp, buffer);
	}
}

/** To get the value of "Tag" field of DO.
\return value of the "Tag" field of the DO. 
\example
DO do_obj(0x19, 0x01, 0x01);
printf("%x", do_obj.getTag());

$$ 19
\endexample
*/
WORD DO::getTag(void) const
{
	return tag;
}

/** To get the value of the "Length" field of DO.
\return value of the "Length" field of the DO. 
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);
printf("%x", do_obj.getLength());

$$ 5
\endexample*/
WORD DO::getLength() const
{
	return length;
}	

/** To get the pointer to the "Value" field of DO.
\return Returns the pointer to the "Value" field of the DO. 
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);
BYTE *value_get = do_obj.getValue();
for(int i = 0; i < do_obj.getLength(); i++) {
	printf("%x ", value_get[i]);
}

$$ 1 2 3 4 5 
\endexample
*/ 
BYTE* DO::getValue(void) const
{
	return value;
}

/** Writes the whole byte stream by concatenating the "TLV" in the same order in the given buffer.
\return Total number of bytes written on the buffer. 
\note It is assumed that given buffer is of sufficient size.
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);

int total_len = do_obj.getTotalLength();
BYTE *byte_stream = (BYTE *) malloc(total_len);
do_obj.getTotalBytes(byte_stream);
for(int i = 0; i < total_len; i++) {
	printf("%x ", byte_stream[i]);
}

$$ 19 5 1 2 3 4 5 
\endexample
*/
WORD DO::getTotalBytes(BYTE *buffer	///< A buffer where to store the Byte-stream of
									///< concatenated TLV fields of the DO.
						) const
{	
	WORD i = 0;
	if (tag < (unsigned) 0xFF) 
		buffer[i++] = (BYTE)tag;
	else {
		buffer[i++] = tag >> 8;
		buffer[i++] = (tag & 0xFF);
	}	

	if(length<=127)  					// If length is 1 byte
		buffer[i++] = (BYTE) length;
	else if(length <= 255) { 			// If length is 2 bytes appending 81 before length by convention
		buffer[i++] = 0x81;
		buffer[i++] = (BYTE) length;
	} 
	else {								// If length is 3 bytes appending 82 before length by convention	
		buffer[i++] = 0x82;
		buffer[i++] = length >> 8;
		buffer[i++] = (length & 0xFF);
	}
	memcpy(buffer+i, value, length);
	return (i+length);
}

/** To get the total length of the DO,
Calculated by summing up the number of bytes in "Tag", "Length" and "Value" fields.
\return Total length of the DO. 
\note Please note the difference in TotalLength and Length of DO. 
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);

int total_len = do_obj.getTotalLength();
printf("%x ", total_len);

$$ 7 
\endexample
*/
WORD DO::getTotalLength(void) const
{
	WORD i = 2 + length; // At least two bytes for T and L. length for the value.
	if (tag > 0xFF) i++;
	if (length > 127) i++;
	if (length > 255) i++;
	return i;
}

/** Overloads the '+' operator and upon invocation returns the new DO
with given BYTE added to "Value" field and "Length" field incremented by one.
\return Reference to the new modified DO.
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);

do_obj = do_obj + 0x06;	// OR do_obj + 0x06;

int total_len = do_obj.getTotalLength();
BYTE *byte_stream = (BYTE *) malloc(total_len);
do_obj.getTotalBytes(byte_stream);
for(int i = 0; i < total_len; i++) {
	printf("%x ", byte_stream[i]);
}

$$ 19 6 1 2 3 4 5 6
\endexample
*/
DO& DO::operator+(const BYTE vchar		///< BYTE to be appended to the "Value" field of the DO.
					)
{
	if ((length % VALUECHUNK) == 0)
		value = (BYTE *) realloc(value, VALUECHUNK*((length + VALUECHUNK)/VALUECHUNK));
	value[length++] = vchar;
	return *this;
}

/** Appends the buffer to the "Value" field and modifies the "Length" field 
according to the length of the buffer to be appended.
\return New modified value of the "Length" field of the DO. 
\note length_arg must contain the exact number of bytes in buffer which has to be appended . 
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);

BYTE buffer[] = {0x0a, 0x0b, 0x0c, 0x0d, 0x0e};
do_obj.appendValue(0x05, buffer);

int total_len = do_obj.getTotalLength();
BYTE *byte_stream = (BYTE *) malloc(total_len);
do_obj.getTotalBytes(byte_stream);
for(int i = 0; i < total_len; i++) {
	printf("%x ", byte_stream[i]);
}

$$ 19 a 1 2 3 4 5 a b c d e 
\endexample
*/
WORD DO::appendValue(WORD length_arg,			///< Length of the buffer to be appended.
						const BYTE *buffer		///< Buffer to be appended.	
						)
{
	value = (BYTE *) realloc(value, VALUECHUNK*((length+length_arg-1+VALUECHUNK)/VALUECHUNK));
	memcpy(value + length, buffer, length_arg);
	length += length_arg;
	return length;
}	

/** Appends the given (Byte stream of)DO given in argument to the end of the "Value" field of current DO
and modifies the "Length" field accordingly, may be used to build constructed DO easily.
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);

DO temp(0x88, 0x0a);
do_obj.appendDO(temp);

int total_len = do_obj.getTotalLength();
BYTE *byte_stream = (BYTE *) malloc(total_len);
do_obj.getTotalBytes(byte_stream);
for(int i = 0; i < total_len; i++) {
	printf("%x ", byte_stream[i]);
}

$$ 19 8 1 2 3 4 5 88 1 a
\endexample
*/
void DO::appendDO(const DO& do_arg		///< DO whose byte-stream is to be appended.
				  )
{
	int i = do_arg.getTotalLength();
	BYTE *buffer = (BYTE *)malloc(i);
	do_arg.getTotalBytes(buffer);
	appendValue(i, buffer);
}

/** Appends the arguments(TLV) to the "Value" field and modifes the "Length" field accordingly.
May be used to build constructed DO easily.
\example
BYTE tag_arg = 0x19;
BYTE len_arg = 0x05;
BYTE value_arg[] = {0x01, 0x02, 0x03, 0x04, 0x05};
DO do_obj(tag_arg, len_arg, value_arg);

BYTE buffer[] = {0x0a, 0x0b, 0x0c, 0x0d, 0x0e};
do_obj.appendDO(0x88, 0x05, buffer);

int total_len = do_obj.getTotalLength();
BYTE *byte_stream = (BYTE *) malloc(total_len);
do_obj.getTotalBytes(byte_stream);
for(int i = 0; i < total_len; i++) {
	printf("%x ", byte_stream[i]);
}

$$ 19 c 1 2 3 4 5 88 5 a b c d e
\endexample
*/
void DO::appendDO(WORD tag_arg,				///< Tag of the DO to be appended.
				  WORD length_arg,			///< Length of the DO to be appended.
				  const BYTE *value_arg		///< Value field of the DO to be appended.
				  )
{
	DO temp(tag_arg, length_arg, value_arg);
	appendDO(temp);
}

/** To get the List of the DOs from the given BYTE stream.
\return List of DOs extracted from the byte stream.
\example
BYTE result[10] = {0xC0, 0x03, 0x01, 0x02, 0x03, 0xC1, 0x04, 0x04, 0x05, 0x06, 0x07};
list<DO> GeneralInfo = getList(10, result);
int len;
BYTE *buffer = NULL;
for(list<DO>::const_iterator p = GeneralInfo.begin(); p != GeneralInfo.end(); p++) {
	len = p->getTotalLength();
	buffer = (BYTE *)realloc(buffer, len);
	p->getTotalBytes(buffer);
	for(int i = 0; i < len; i++)
		printf("%x", buffer[i]);
	printf("\n");
}

$$ C0 3 1 2 3
$$ C1 4 4 5 6 7
\endexample
*/
list<DO> getList(int length, const BYTE* byte_stream)
{
	int offset;
	list<DO> list;
	for(offset = 0; offset < length; ) {
		DO temp(byte_stream + offset);
		list.push_back(temp);
		offset += temp.getTotalLength();
	}
    return list;
}
